package game;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import datastructures.Coordinate;
import datastructures.Edge;
import datastructures.EdgeWord;
import datastructures.Word;
import datastructures.World;

/**
 * @author Administrator
 * 
 */
public class MainController implements ActionListener, MouseListener,
		MouseMotionListener {

	/** Action for creating a new world */
	public static final String MENU_ACTION_NEW = "NEW_WORLD";

	/** Action for Exiting the program */
	public static final String MENU_ACTION_EXIT = "EXIT";

	/** Action for Saving the world */
	public static final String MENU_ACTION_SAVE = "SAVE";

	/** Action for opening a new world */
	public static final String MENU_ACTION_OPEN_FILE = "OPEN_FILE";

	/** Action for creating a new world */
	public static final String BUTTON_ACTION_NEW_WORD = "NEW_WORD";

	/** Action for connecting words */
	public static final String BUTTON_ACTION_CONNECT_WORDS = "CONNECT_WORDS";

	/** Action for editing a word */
	public static final String BUTTON_ACTION_EDIT_WORD = "EDIT_WORD";

	/** Action for deleting a word */
	public static final String BUTTON_ACTION_DELETE = "DELETE_WORD";

	/** Action ok action when creating a new word */
	public static final String NEW_WORD_ACTION_OK = "OK";

	/** Action cancel action when creating a new word */
	public static final String NEW_WORD_ACTION_CANCEL = "CANCEL";

	/** Action for editing a word */
	public static final String EDITING_ACTION = "EDITING";

	private static final File CURRENT_DIR = new File(".");

	private MainModel model;

	private boolean makingNewWord = false;

	private boolean editingWord = false;

	private boolean connectingWords = false;

	private boolean canDrag = false;

	private boolean deleting = false;

	private JFrame mainFrame = null;

	private static final Cursor crossCursor = new Cursor(
			Cursor.CROSSHAIR_CURSOR);

	private static final Cursor standardCursor = new Cursor(
			Cursor.DEFAULT_CURSOR);

	private Word editedWord;

	/**
	 * Constructor creating a new controller
	 * 
	 * @param m
	 */
	public MainController(MainModel m) {
		this.model = m;

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals(MENU_ACTION_EXIT)) {
			System.exit(1);
		} else if (e.getActionCommand().equals(MENU_ACTION_NEW)) {
			this.model.setWorld(new World());
			this.model.changed();
		} else if (e.getActionCommand().equals(MENU_ACTION_SAVE)) {
			saveWorld();
		} else if (e.getActionCommand().equals(EDITING_ACTION)) {
			this.model.setEditingMode(!this.model.getEditingMode());
			this.model.changed();
		} else if (e.getActionCommand().equals(MENU_ACTION_OPEN_FILE)) {
			loadWorldDialog();
		} else if (e.getActionCommand().equals(BUTTON_ACTION_NEW_WORD)) {
			this.editedWord = new Word("");
			WordDialog editWordDialog;
			if (!doingSomething()) {
				if (this.mainFrame == null) {
					editWordDialog = new WordDialog(this.editedWord);
				} else {
					editWordDialog = new WordDialog(this.mainFrame, true,
							this.editedWord);
				}
				editWordDialog.setVisible(true);

				if (editWordDialog.isCancel()) {
					// donothing
				} else if (this.editedWord.isMiddle()) {
					// Should makes some checks here
					// Like if there already is a middleword and if the word
					// already existed
					// and show a errorbox
					this.model.addWordToWindow(this.editedWord);
					this.model.changed();
				} else {
					this.makingNewWord = true;
				}
			}
		} else if (e.getActionCommand().equals(BUTTON_ACTION_EDIT_WORD)) {
			if (!doingSomething()) {
				this.editingWord = true;
			}
		} else if (e.getActionCommand().equals(BUTTON_ACTION_DELETE)) {
			if (!doingSomething()) {
				this.deleting = true;
			}
		} else if (e.getActionCommand().equals(BUTTON_ACTION_CONNECT_WORDS)) {
			if (!doingSomething()) {
				this.connectingWords = true;
				this.editedWord = null;
			}
		}

	}

	public void loadWorldDialog() {

		JFileChooser fc = new JFileChooser(CURRENT_DIR);
		File f = null;
		int returnVal = fc.showSaveDialog(this.mainFrame);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			f = fc.getSelectedFile();
		} else {
			return;
		}
		
		try {
			loadWorld(f);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void loadWorld(File f) throws IOException, ClassNotFoundException {
		FileInputStream fileIn = new FileInputStream(f);
		ObjectInputStream in = new ObjectInputStream(fileIn);
		World w = (World) in.readObject();
		in.close();
		fileIn.close();
		this.model.setWorld(w);
		this.model.changed();
	}
	
	public void loadWorld(InputStream is) throws IOException, ClassNotFoundException {
		
		ObjectInputStream in = new ObjectInputStream(is);
		World w = (World) in.readObject();
		in.close();
		this.model.setWorld(w);
		this.model.changed();
	}

	private void saveWorld() {
		FileOutputStream fileOut = null;
		ObjectOutputStream out = null;
		try {
			JFileChooser fc = new JFileChooser(CURRENT_DIR);
			File f = null;
			int returnVal = fc.showSaveDialog(this.mainFrame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				f = fc.getSelectedFile();
			} else {
				return;
			}

			if (!f.exists()) {
				f.createNewFile();
			}

			fileOut = new FileOutputStream(f);

			out = new ObjectOutputStream(fileOut);
			out.writeObject(this.model.getWorld());
			out.close();
			fileOut.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private boolean doingSomething() {
		return (this.makingNewWord || this.connectingWords || this.editingWord || this.deleting);
	}

	/**
	 * Sets the JFrame that is used as a parent when showing dialogs
	 * 
	 * @param mainFrame
	 */
	public void setMainFrame(JFrame mainFrame) {
		this.mainFrame = mainFrame;

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e) {
		if (e.getComponent() instanceof MapPanel) {
			MapPanel m = (MapPanel) e.getComponent();
			Coordinate mouseCoordinate = new Coordinate(e.getPoint().x, e
					.getPoint().y);
			Coordinate selectedCoordinate = m.getNewCoordinate(mouseCoordinate);
			if(model.isWindowUnlocked(selectedCoordinate)){
				this.model.setCurrentWindowCordinates(selectedCoordinate);
				this.model.changed();
			}
			
		} else if (this.makingNewWord) {

			this.makingNewWord = false;
			e.getComponent().setCursor(standardCursor);

			Coordinate newCoordinate = new Coordinate(e.getPoint().x, e
					.getPoint().y);
			this.editedWord.setCoordinates(newCoordinate);

			if (newCoordinate.getX() > EdgeWord.EAST_EDGE
					&& newCoordinate.getY() > EdgeWord.NORTH_EDGE
					&& newCoordinate.getY() < EdgeWord.SOUTH_EDGE) {
				EdgeWord newEdgeWord = new EdgeWord(this.editedWord,
						EdgeWord.EAST_EDGE);
				System.out.print(this.model.addEdgeWord(newEdgeWord));
				this.model.changed();
			} else if (newCoordinate.getX() < EdgeWord.WEST_EDGE
					&& newCoordinate.getY() > EdgeWord.NORTH_EDGE
					&& newCoordinate.getY() < EdgeWord.SOUTH_EDGE) {
				EdgeWord newEdgeWord = new EdgeWord(this.editedWord,
						EdgeWord.WEST_EDGE);
				System.out.print(this.model.addEdgeWord(newEdgeWord));
				this.model.changed();
			} else if (newCoordinate.getY() < EdgeWord.NORTH_EDGE
					&& newCoordinate.getX() > EdgeWord.WEST_EDGE
					&& newCoordinate.getY() < EdgeWord.EAST_EDGE) {
				EdgeWord newEdgeWord = new EdgeWord(this.editedWord,
						EdgeWord.NORTH_EDGE);
				System.out.print(this.model.addEdgeWord(newEdgeWord));
				this.model.changed();
			} else if (newCoordinate.getY() > EdgeWord.SOUTH_EDGE
					&& newCoordinate.getX() > EdgeWord.WEST_EDGE
					&& newCoordinate.getY() < EdgeWord.EAST_EDGE) {
				EdgeWord newEdgeWord = new EdgeWord(this.editedWord,
						EdgeWord.SOUTH_EDGE);
				System.out.print(this.model.addEdgeWord(newEdgeWord));
				this.model.changed();
			} else {
				this.model.addWordToWindow(this.editedWord);
			}

			// Should check if the word already existed and show an errorBox

		} else if (this.editingWord) {
			Component c = e.getComponent();
			this.editingWord = false;
			e.getComponent().setCursor(standardCursor);

			if (c instanceof GameFieldPanel) {
				this.editedWord = ((GameFieldPanel) c).getWord(new Coordinate(e
						.getPoint().x, e.getPoint().y));
				WordDialog editWordDialog;
				if (this.editedWord != null) {
					if (this.mainFrame == null) {
						editWordDialog = new WordDialog(this.editedWord);
					} else {
						editWordDialog = new WordDialog(this.mainFrame, true,
								this.editedWord);
					}
					editWordDialog.setVisible(true);
					this.model.changed();

				}

			}

		} else if (this.deleting) {
			Component c = e.getComponent();
			this.deleting = false;
			e.getComponent().setCursor(standardCursor);

			if (c instanceof GameFieldPanel) {
				this.editedWord = ((GameFieldPanel) c).getWord(new Coordinate(e
						.getPoint().x, e.getPoint().y));

				if (this.editedWord != null) {
					int answer = JOptionPane.CANCEL_OPTION;

					answer = JOptionPane.showConfirmDialog(this.mainFrame,
							"Do you really want to delete the word "
									+ this.editedWord.getWordText(), "Delete?",
							JOptionPane.OK_CANCEL_OPTION);
					if (answer == JOptionPane.OK_OPTION) {
						this.model.deleteWord(this.editedWord);
						this.model.changed();
					}

				}

			}

		} else if (this.connectingWords) {
			Word leftWord = this.editedWord;
			Component c = e.getComponent();
			e.getComponent().setCursor(standardCursor);

			if (c instanceof GameFieldPanel) {
				this.editedWord = ((GameFieldPanel) c).getWord(new Coordinate(e
						.getPoint().x, e.getPoint().y));
				if (this.editedWord != null) {
					if (leftWord != null && !leftWord.equals(this.editedWord)) {
						Edge edge = new Edge(leftWord, this.editedWord);
						leftWord.addEdge(edge);
						this.editedWord.addEdge(edge);
						System.out.println(leftWord.getWordText());
						System.out.println(this.editedWord.getWordText());
						this.model.changed();
						this.connectingWords = false;
					}
				} else {
					this.editedWord = leftWord;
				}

			}
		} else {
			if (e.getClickCount() > 1) {
				this.editingWord = true;
				mouseClicked(e);
			}
		}

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e) {
		// Not used
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e) {
		// Not used

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e) {

		if (!doingSomething()) {
			Component c = e.getComponent();
			if (c instanceof GameFieldPanel) {
				this.editedWord = ((GameFieldPanel) c).getWord(new Coordinate(e
						.getPoint().x, e.getPoint().y));
				if (this.editedWord != null) {
					this.canDrag = true;
				} else {
					this.canDrag = false;
				}
			}
		}

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e) {
		this.canDrag = false;

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
	 */
	public void mouseDragged(MouseEvent e) {
		if (this.canDrag && !this.editedWord.isMiddle()) {
			Point p = e.getPoint();
			Coordinate c = new Coordinate(p.x, p.y);
			this.editedWord.setCoordinates(c);
			this.model.changed();
		}

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
	 */
	public void mouseMoved(MouseEvent e) {
		if (doingSomething()) {
			e.getComponent().setCursor(crossCursor);
		}

	}

}
